/***
 * Windows Tray Icon
 * -----------------
 *
 * Written by Jan Struyf
 *
 *  jan.struyf@cs.kuleuven.ac.be
 *  http://jeans.studentenweb.org/java/trayicon/trayicon.html
 *
 * Please mail me if you
 *	- 've found bugs
 *	- like this program
 *	- don't like a particular feature
 *	- would like something to be modified
 *
 * I always give it my best shot to make a program useful and solid, but
 * remeber that there is absolutely no warranty for using this program as
 * stated in the following terms:
 *
 * THERE IS NO WARRANTY FOR THIS PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE
 * LAW. THE COPYRIGHT HOLDER AND/OR OTHER PARTIES WHO MAY HAVE MODIFIED THE
 * PROGRAM, PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
 * TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
 * PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
 * REPAIR OR CORRECTION.
 *
 * IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL ANY COPYRIGHT HOLDER,
 * OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM,
 * BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
 * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
 * PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
 * INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
 * PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
 * PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *
 * May the Force be with you... Just compile it & use it!
 */

package demo.awt;

import java.awt.*;
import java.awt.event.*;
import java.io.*;

import com.jeans.trayicon.*;

// Demo app for Java Tray Icon
public class TestTrayIcon extends Frame {

    public final static int ENABLE_ITEM = 0;
    public final static int BOLD_ITEM = 1;
    public final static int CHECK_ITEM = 2;

	// App uses 4 icons
	private TrayIconButton icon[];
	// Buttons for exit, hide window and about box
	private Button exit_bt, hide_bt, about_bt;

	public TestTrayIcon() throws TrayIconException, InterruptedException {
		// Create new frame with title
		super("Test Icon");
		setBackground(SystemColor.control);
		// Set callback method to send windows messages through Tray Icon library (see WindowsMessageCallback)
		WindowsTrayIcon.setWindowsMessageCallback(new WindowsMessageCallback());
		// Layout stuff
		setLayout(new GridLayout(0,1,3,3));
		// Load 16x16 icon gif
		setIconImage(loadImage("Duke16.gif"));
		// Create 4 checkboxes with icons: Printer, Battery, France & MS-DOS
		icon = new TrayIconButton[4];
		add(icon[0] = new TrayIconButton("Duke",loadImage("Duke16.gif"),16,16));		
		add(icon[1] = new TrayIconButton("Printer",loadImage("Printer.gif"),16,16));
		add(icon[2] = new TrayIconButton("France",loadImage("France.gif"),16,16));
		add(icon[3] = new TrayIconButton("MS-DOS",loadImage("MsDos.gif"),16,16));
		// Tray Icon left mouse button event restores the main window (make it visible again/requests focus
		RestoreListener listener = new RestoreListener(false);
		for (int k = 0; k < 4; k++) icon[k].addActionListener(listener);
		// Panel with exit, hide & about button - With hide you can hide the main window and restore it
		// by clicking on one of the Tray Icons
		Panel panel = new Panel();
		panel.setLayout(new GridLayout(1,0,3,3));
		panel.add(exit_bt = new Button("Exit"));
		exit_bt.addActionListener(new ExitListener());
		panel.add(hide_bt = new Button("Hide"));
		hide_bt.addActionListener(new HideListener());
		panel.add(about_bt = new Button("About"));
		about_bt.addActionListener(new AboutListener());
		// Each icon has it's own popup menu
		// Because the menu's contain state information, you can't use the same menu for different icons)
		icon[0].setPopup(makePopup());
		icon[1].setPopup(makePopup());
		icon[2].setPopup(makePopup());
		icon[3].setPopup(makePopup());
		add(panel);
		pack();
		validate();
		// Exit if click on close button
		addWindowListener(new ExitWindowListener());
	}

	// Create the popup menu for each Tray Icon (on right mouse click)
	public TrayIconPopup makePopup() {
		// Make new popup menu
		TrayIconPopup popup = new TrayIconPopup();
		// Add show, about, submenu, separator & exit item
		TrayIconPopupSimpleItem item = new TrayIconPopupSimpleItem("&Show");
		item.setDefault(true);
		// Each menu item can have it's own ActionListener		
		item.addActionListener(new RestoreListener(true));
		popup.addMenuItem(item);
		item = new TrayIconPopupSimpleItem("&About");
		item.addActionListener(new AboutListener());
		popup.addMenuItem(item);
		// Create a submenu with title enable and items check 1 & check 2
		TrayIconPopup sub = new TrayIconPopup("&Options");
		// Create and add two checkbox menu items
		TrayIconPopupCheckItem test = new TrayIconPopupCheckItem("Test");
		sub.addMenuItem(test);
		sub.addMenuItem(new TrayIconPopupSeparator());
		TrayIconPopupCheckItem ena = new TrayIconPopupCheckItem("Enable Test");
        	ena.addActionListener(new ChangeMenuListener(test, ENABLE_ITEM));			
		ena.setCheck(true);
		sub.addMenuItem(ena);
		TrayIconPopupCheckItem bold = new TrayIconPopupCheckItem("Bold Test");
	        bold.addActionListener(new ChangeMenuListener(test, BOLD_ITEM));
		sub.addMenuItem(bold);
		TrayIconPopupCheckItem check = new TrayIconPopupCheckItem("Check Test");
        	check.addActionListener(new ChangeMenuListener(test, CHECK_ITEM));
	        test.addActionListener(new ChangeMenuListener(check, CHECK_ITEM));
		sub.addMenuItem(check);		
		// Add submenu to the main menu
		popup.addMenuItem(sub);
		// Add a separator		
		popup.addMenuItem(new TrayIconPopupSeparator());
		// Add exit item
		item = new TrayIconPopupSimpleItem("E&xit");
		item.addActionListener(new ExitListener());
		popup.addMenuItem(item);
		return popup;
	}

	// Load a gif image (used for loading the 16x16 icon gifs)
	public static Image loadImage(String fileName) {
		return Toolkit.getDefaultToolkit().getImage("demo"+File.separator+"images"+File.separator+fileName);
	}

	// Main proc
	public static void main(String[] args) {
		try {
			String appName = "TestTray";
			// First check if there's another instance of our app running by sending a windows message
			// to a hidden icon window "TestTray" - each Tray Icon app has a hidden window that receives
			// the mouse/menu messages for it's Tray Icons
			long result = WindowsTrayIcon.sendWindowsMessage(appName, 1234);
			if (result != -1) {
				// If window exists, there's already an instance of our app running
				// Print message and exit (other app will restore its window when receiving
				// our message - see WindowsMessageCallback
				System.out.println("Already running other instance of "+appName+" (returns: "+result+")");
				return;
			}
			// Init the Tray Icon library given the name for the hidden window
			WindowsTrayIcon.initTrayIcon(appName);
			// Show our main window
			TestTrayIcon test = new TestTrayIcon();
			TestTrayIcon.centerDialog(test);
			test.show();
		} catch (TrayIconException e) {
			System.out.println("Error: "+e.getMessage());
		} catch (InterruptedException e) {
		}
	}
	
    public void doExit() {	
        System.out.println("Exit selected.");
		// Free all Tray Icon resources - always call this on exit
		WindowsTrayIcon.cleanUp();
        // Exit application
		System.exit(0);
    }
	
	public void doHide(boolean exitOnFail) {
        System.out.println("Hide selected.");
		// Check if there's a Tray Icon visible
		// Hide works only when there's an icon in the system tray
        boolean visible = true;
        for (int k = 0; k < 4; k++) {
		    if (icon[k].testVisible()) visible = false;
		}
		setVisible(visible);
        if (visible == true) {
            System.out.println("Hide works only when there's an icon in the system tray.");            
            if (exitOnFail) doExit();
        }
    }
        
	public static void centerDialog(Window frame) {
		Dimension dialogSize = frame.getSize();
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		frame.setLocation(screenSize.width/2 - dialogSize.width/2,
		                  screenSize.height/2 - dialogSize.height/2);
	}    
	
	// Callback listener handles incomming windows messages. In this demo, a windows message is send when the
	// user tries to start a second instance of the demo app. You can try this one by opening two MS-DOS prompts
	// and say in each one "java demo.awt.TestTrayIcon"
	// MS-DOS 1:
	// 	C:\TrayIcon>java demo.awt.TestTrayIcon
	//	...
	//	Other instance started (parameter: 1234)
	//
	// MS-DOS 2:
	// 	C:\TrayIcon>java demo.awt.TestTrayIcon
	// 	Already running other instance of TestTray (returns: 4321)
	private class WindowsMessageCallback implements TrayIconCallback {

		public int callback(int param) {
			// Param contains the integer value send with sendWindowsMessage(appName, param)
			System.out.println("Other instance started (parameter: "+param+")");
			setVisible(true);
			requestFocus();
			// Return integer value to other process
			return 4321;
		}

	}

	// Callback listener handles exit button / exit popup menu
	private class ExitListener implements ActionListener {

		public void actionPerformed(ActionEvent evt) {
		    doExit();
		}

	}

	// Callback listener handles restore (click left on any icon / show popup menu)
	private class RestoreListener implements ActionListener {

        protected boolean from_menu;

        public RestoreListener(boolean fromMenu) {
            from_menu = fromMenu;
        }

		public void actionPerformed(ActionEvent evt) {
			if (from_menu) System.out.println("Restore selected..");
			else System.out.println("Tray icon button pressed..");
			// Make main window visible if it was hidden
			setVisible(true);
			// Request input focus
			requestFocus();
		}

	}

	// Callback listener for hide button
	private class HideListener implements ActionListener {

		public void actionPerformed(ActionEvent evt) {
		    doHide(false);
		}

	}

	// Callback listener handles about button
	private class AboutListener implements ActionListener {

		public void actionPerformed(ActionEvent evt) {
			System.out.println("About selected..");
			TrayAboutBox box = new TrayAboutBox(TestTrayIcon.this);
			centerDialog(box);
			box.show();
		}

	}

	// Callback listener handles window close button
	private class ExitWindowListener extends WindowAdapter {

		public void windowClosing(WindowEvent evt) {
			// Free all Tray Icon resources - always call this on exit
			WindowsTrayIcon.cleanUp();
			System.exit(0);
		}
	}
	
	// Callback listener handles restore (click left on any icon / show popup menu)
	private class ChangeMenuListener implements ActionListener {

        protected TrayIconPopupCheckItem m_Item;
        protected int m_Change;

        public ChangeMenuListener(TrayIconPopupCheckItem item, int change) {
            m_Item = item;
            m_Change = change;
        }

		public void actionPerformed(ActionEvent evt) {
		    TrayIconPopupCheckItem source = (TrayIconPopupCheckItem)evt.getSource();
			boolean value = source.getCheck();
			switch (m_Change) {
			    case ENABLE_ITEM:
			        m_Item.setEnabled(value); break;
			    case BOLD_ITEM:
			        m_Item.setDefault(value); break;
			    case CHECK_ITEM:
			        m_Item.setCheck(value); break;
			}
		}
	}		
}

// Panel with checkbox, string and icon
class TrayIconButton extends Panel {

	// The checkbox for enabling this Tray Icon
	Checkbox button;
	// The Tray Icon's class
	WindowsTrayIcon icon;

	// Create new panel with checkbox, string and icon
	// Name = Printer/France/Battery/MS-DOS
	// Image = 16x16 Icon gif
	// Wd = 16, Hi = 16
	public TrayIconButton(String name, Image image, int wd, int hi) throws TrayIconException, InterruptedException {
		setLayout(new BorderLayout(3,3));
		// Add icon image in ImageDisplayer
		add(new ImageDisplayer(image, wd, hi), BorderLayout.EAST);
		// Add checkbox for enabling Tray Icon
		add(button = new Checkbox(name), BorderLayout.CENTER);
		button.addItemListener(new ToggleListener());
		// Create Tray Icon and set tooltip
		icon = new WindowsTrayIcon(image, wd, hi);
		icon.setToolTipText(name);
	}

	// Add popup menu to Tray Icon
	public void setPopup(TrayIconPopup popup) {
		icon.setPopup(popup);
	}

	// Add action listener to Tray Icon
	public void addActionListener(ActionListener listener) {
		icon.addActionListener(listener);
	}

	// Icon visible?
	public boolean testVisible() {
		return icon.isVisible();
	}

	// Listener for checkbox
	// Make icon visible/invisible depending on checkbox state
	private class ToggleListener implements ItemListener {

		public void itemStateChanged(ItemEvent evt) {
			icon.setVisible(evt.getStateChange() == ItemEvent.SELECTED);
		}

	}

}

// Canvas for displaying images with given size
class ImageDisplayer extends Canvas {

	// The image to display
	Image image;
	// The images size
	int wd, hi;

	// Create new canvas with given image and size
	public ImageDisplayer(Image image, int wd, int hi) {
		this.image = image;
		this.wd = wd;
		this.hi = hi;
	}

	// Pref size for layout manager
	public Dimension getPreferredSize() {
		return new Dimension(wd, hi);
	}

	// Min size for layout manager
	public Dimension getMinimumSize() {
		return new Dimension(wd, hi);
	}

	// Redraw image (don't clear background first!)
	public void update(Graphics g) {
		paint(g);
	}

 	// Redraw image
	public void paint(Graphics g) {
		g.drawImage(image, 0, 0, this);
	}
}

